/*
 * Copyright 2007 National Bioinformatics Network
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package dgbgui.command;

import java.util.Observable;
import java.util.Stack;

import dgbgui.selection.SelectionManager;

/**
 * The command stack contains the executed commands and handles
 * delegating undo/redo to the commands on the stack
 * @author bjnortier
 *
 */
/**
 * @author bjnortier
 *
 */
public class CommandStack extends Observable  {
    
    // Private for singleton
    private CommandStack() {
    }
    
    public static CommandStack getInstance() {
        if ( m_instance == null ) {
            m_instance = new CommandStack();
        }
        return m_instance;
    }
    
    /**
     * Clear the command stack (useful on "new")
     */
    synchronized public void clear() {
        SelectionManager.getInstance().clear();
        m_undoStack.clear();
        m_redoStack.clear();
        
        // Notify
        setChanged();
        notifyObservers();
    }
    
    
    synchronized public void execute(Command command) {
        SelectionManager.getInstance().clear();
        
        // Execute the command and add it to the undo stack.
        // Also clear the redo stack on external execution of a command
        command.execute();
        if ( command.isUndoable() ) {
            m_undoStack.push(command);
        }
        m_redoStack.clear();
        
        // Notify
        setChanged();
        notifyObservers();
    }
    

    
    /**
     * Undo a command
     */
    synchronized public void undo() {
        SelectionManager.getInstance().clear();
        // Action validation should ensure that undo is not available
        // when the undo stack is clear
        assert ( m_undoStack.size() > 0 );
        
        // Remove the command from the undo stack, undo it and
        // add it to the redo stack.
        Command commandToUndo = m_undoStack.pop();
        commandToUndo.undo();
        m_redoStack.push(commandToUndo);
        
        // Notify
        setChanged();
        notifyObservers();
    }
    
    /**
     * Redo a command
     */
    synchronized public void redo() {
        SelectionManager.getInstance().clear();
        // Action validation should ensure that redo is not available
        // when the redo stack is clear
        assert ( m_redoStack.size() > 0 );
        
        // Remove the command from the redo stack, redo it and
        // add it to the undo stack.
        Command commandToRedo = m_redoStack.pop();
        commandToRedo.redo();
        m_undoStack.push(commandToRedo);
        
        // Notify
        setChanged();
        notifyObservers();
    }
    

    public int getUndoStackSize() {
        return m_undoStack.size();
    }
    
    public int getRedoStackSize() {
        return m_redoStack.size();
    }
    
    public void setSavePoint() {
        m_commandAtSavePoint = m_undoStack.peek();
        setChanged();
        notifyObservers();
    }
    
    /**
     * @return whether the command stack is at the save point, determined
     * by whether top command at the top of the stack is the command
     * that was at the top when the model was saved
     */
    public boolean atSavePoint() {
        if ( m_undoStack.size() > 0 ) {
            return (m_commandAtSavePoint == m_undoStack.peek());
        } else {
            return false;
        }
    }
    
    /** 
     * Update the undo/redo actions depending on the state of the command stack
     */

    
    private Stack<Command> m_undoStack = new Stack<Command>();
    private Stack<Command> m_redoStack = new Stack<Command>();
    
    /**
     * The command stack stores the save point by storing the current command
     * on save.
     */
    private Command m_commandAtSavePoint = null;
    
    
    private static CommandStack m_instance;


}
